package com.smartalarmclock.Model;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map.Entry;
import java.util.Set;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.smartalarmclock.Model.ITimeFrame.TimeInWeek;
import com.smartalarmclock.Model.Reference.DeleteMethod;
import com.smartalarmclock.Model.Exceptions.DataEntityDoesNotExistException;
import com.smartalarmclock.Model.Exceptions.InvalidDeleteDataEntityOperationException;
import com.smartalarmclock.Model.Exceptions.InvalidNullReferenceOnDeleteException;
import com.smartalarmclock.Model.Exceptions.InvalidReferenceToRemoveException;
import com.smartalarmclock.Model.Exceptions.InvalidSaveDataEntityOperationException;
import com.smartalarmclock.Model.Exceptions.NamedProfileAlreadyExistsException;
import com.smartalarmclock.Model.Exceptions.ReferenceCannotBeNullException;
import com.smartalarmclock.Model.IProfile;

final class Profile extends RulesItem implements IProfile, IReferencingMultipleDataEntity {

	// Database Constants

	public static final String ProfileTableName = "Profile";	
	private static final String c_ProfileColumnNameName = "Name";
	private static final String c_ProfileColumnNameIsActive = "IsActive";
	private static final String c_ProfileColumnNameNonMatchingAutoResponse = "NonMatchingAutoResponse";
	
	private static final String[] c_QueryAllColumnsProfile = new String[] {
		c_ColumnNameId, c_ProfileColumnNameName, c_ProfileColumnNameIsActive, c_ProfileColumnNameNonMatchingAutoResponse };
	
	private static final String ProfileToContactsTableName = "ProfileToContacts";
	private static final String c_ProfileToContactsColumnNameProfile = "Profile";
	private static final String c_ProfileToContactsColumnNameContact = "Contact";
	
	private static final String[] c_QueryAllColumnsProfileToContacts = new String[] {
		c_ProfileToContactsColumnNameProfile, c_ProfileToContactsColumnNameContact };
	
	// Members
	
	private String m_Name;
	private boolean m_IsActive;
	private final SingleReference<IAutoResponse> m_NonMatchingAutoResponseReference;
	private final MultipleReference<IContact> m_ContactReferences;
	
	// Constructors
	
	/**
	 * Constructor
	 * @param name the name of the profile
	 * @param isActive whether the profile is active or not
	 * @param alarmClock the alarm clock of the profile
	 * @param matchingAutoResponse the auto response for matching contacts
	 * @param nonMatchingAutoResponse the auto response for non matching contacts
	 */
	protected Profile(
		String name,
		boolean isActive,
		IAlarmClock alarmClock,
		IAutoResponse matchingAutoResponse,
		IAutoResponse nonMatchingAutoResponse) {
		super(alarmClock, matchingAutoResponse);
		m_NonMatchingAutoResponseReference = new SingleReference<IAutoResponse>(
			this, nonMatchingAutoResponse, false, AutoResponse.class, DeleteMethod.Delete);
		m_ContactReferences = new MultipleReference<IContact>(this, Contact.class, DeleteMethod.NoAction);
		Initialize(name, isActive);
	}
	
	/**
	 * Constructor
	 * @param id the id of the profile
	 * @param name the name of the profile
	 * @param isActive whether the profile is active or not
	 * @param alarmClock the alarm clock of the profile
	 * @param matchingAutoResponse the auto response for matching contacts
	 * @param nonMatchingAutoResponse the auto response for non matching contacts
	 */
	private Profile(
		int id,
		String name,
		boolean isActive,
		IAlarmClock alarmClock,
		IAutoResponse matchingAutoResponse,
		IAutoResponse nonMatchingAutoResponse) {
		super(id, alarmClock, matchingAutoResponse);
		m_NonMatchingAutoResponseReference = new SingleReference<IAutoResponse>(
			this, nonMatchingAutoResponse, false, AutoResponse.class, DeleteMethod.Delete);
		m_ContactReferences = new MultipleReference<IContact>(this, Contact.class, DeleteMethod.NoAction);
		Initialize(name, isActive);
	}
	
	// IProfile implementation

	/**
	 * @return the name of the profile
	 */
	@Override
	public String GetName() {
		return m_Name;
	}

	/**
	 * Sets the name of the profile
	 * @param name the new name
	 */
	@Override
	public void SetName(String name) {
		m_Name = name;
		SetUpdated();
	}

	/**
	 * @return whether the profile is active or not
	 */
	@Override
	public boolean IsActive() {
		return m_IsActive;
	}

	/**
	 * Sets the active state
	 * @param isActive the new active state
	 */
	@Override
	public void SetActive(boolean isActive) {
		m_IsActive = isActive;
		SetUpdated();
	}

	/**
	 * @return the auto response for non matching contacts
	 */
	@Override
	public IAutoResponse GetNonMatchingAutoResponse() {
		return m_NonMatchingAutoResponseReference.GetReference();
	}
	
	/**
	 * @param context the context of the request (the originating Activity)
	 * @return a set of the profile's contact list
	 */
	public Set<IContact> GetContacts(Context context) {
		return m_ContactReferences.GetReferences(context);
	}
	
	/**
	 * Adds a contact to the profile
	 * @param context the context of the request (the originating Activity)
	 * @param contact the contact to add
	 */
	public void AddContact(Context context, IContact contact) {
		m_ContactReferences.Add(context, contact);
		SetUpdated();
	}
	
	/**
	 * Removes a contact from the profile
	 * @param context the context of the request (the originating Activity)
	 * @param contact the contact to remove
	 */
	public void RemoveContact(Context context, IContact contact) {
		m_ContactReferences.Remove(context, contact);
		SetUpdated();
	}
	
	// Public Methods
	
	/**
	 * @return the CREATE TABLE statement of the Profile table
	 */
	static String GetSqlStatementCreateProfileTable() {
		return "CREATE TABLE " + ProfileTableName + " (" +
				c_ColumnNameId + 								" INTEGER PRIMARY KEY, " +
				c_ProfileColumnNameName + 						" TEXT NOT NULL, " +
				c_ProfileColumnNameIsActive +					" INTEGER NOT NULL, " +
				c_ProfileColumnNameNonMatchingAutoResponse +	" INTEGER NOT NULL," +
				"FOREIGN KEY(" + c_ProfileColumnNameNonMatchingAutoResponse + ") REFERENCES " + AutoResponse.AutoResponseTableName + "(" + c_ColumnNameId + "))";
	}
	
	/**
	 * @return the DROP TABLE statement of the Profile table
	 */
	static String GetSqlStatementDropProfileTable() {
		return "DROP TABLE IF EXISTS " + ProfileTableName;
	}
	
	/**
	 * @return the CREATE TABLE statement of the ProfileToContacts table
	 */
	static String GetSqlStatementCreateProfileToContactsTable() {
		return "CREATE TABLE " + ProfileToContactsTableName + " (" +
				c_ProfileToContactsColumnNameProfile +	" INTEGER NOT NULL, " +
				c_ProfileToContactsColumnNameContact +	" INTEGER NOT NULL," +
				"PRIMARY KEY (" + c_ProfileToContactsColumnNameProfile + ", " + c_ProfileToContactsColumnNameContact + ")," +
				"FOREIGN KEY(" + c_ProfileToContactsColumnNameProfile   + ") REFERENCES " + Profile.ProfileTableName + "(" + c_ColumnNameId + ")," +
				"FOREIGN KEY(" + c_ProfileToContactsColumnNameContact + ") REFERENCES " + Contact.ContactTableName + "(" + c_ColumnNameId + "))";
	}
	
	/**
	 * @return the DROP TABLE statement of the ProfileToContacts table
	 */
	static String GetSqlStatementDropProfileToContactsTable() {
		return "DROP TABLE IF EXISTS " + ProfileToContactsTableName;
	}
	
	/**
	 * Queries for a profile with the matching id
	 * @param context the context of the request
	 * @param id the id to query for
	 * @return the matching profile.
	 * @throws DataEntityDoesNotExistException if the profile does not exist
	 */
	static Profile GetProfileById(Context context, int id) throws DataEntityDoesNotExistException {
		return GetSingleProfile(context, c_ColumnNameId + "=" + id);
	}
	
	/**
	 * Queries for a profile with the matching id
	 * @param context the context of the request
	 * @param id the id to query for
	 * @return the matching profile, or null if does not exist.
	 */
	static Profile TryGetProfileById(Context context, int id) {
		try {
			return GetProfileById(context, id);
		} catch (DataEntityDoesNotExistException e) {
			return null;
		}
	}
	
	/**
	 * Queries for a profile with the matching name
	 * @param context the context of the request
	 * @param name the name to query for
	 * @return the matching profile.
	 * @throws DataEntityDoesNotExistException if the profile does not exist
	 */
	static Profile GetProfileByName(Context context, String name) throws DataEntityDoesNotExistException {
		return GetSingleProfile(context, c_ProfileColumnNameName + " LIKE \"" + name + "\"");
	}
	
	/**
	 * Queries for a profile with the matching name
	 * @param context the context of the request
	 * @param name the name to query for
	 * @return the matching profile, or null if does not exist.
	 */
	static Profile TryGetProfileByName(Context context, String name) {
		try {
			return GetProfileByName(context, name);
		} catch (DataEntityDoesNotExistException e) {
			return null;
		}
	}
	
	/**
	 * Queries for a profile with the matching alarm clock
	 * @param context the context of the request
	 * @param alarmClock the alarm clock to query for
	 * @return the matching profile.
	 * @throws DataEntityDoesNotExistException if the profile does not exist
	 */
	static Profile GetProfileByAlarmClock(Context context, IAlarmClock alarmClock) throws DataEntityDoesNotExistException {
		SQLiteDatabase db = OpenDBForReading(context);
		
		Cursor rulesItemCursor = GetRulesItemValuesByAlarmClock(db, alarmClock);
		rulesItemCursor.moveToNext();
		int rulesItemId = rulesItemCursor.getInt(rulesItemCursor.getColumnIndex(c_ColumnNameId));
		IAutoResponse matchingAutoResponse = GetMatchingAutoResponseFromCursor(context, rulesItemCursor);
		rulesItemCursor.close();
		db.close();
		
		return GetProfileById(context, rulesItemId, alarmClock, matchingAutoResponse);
	}
	
	/**
	 * Queries for a profile with the matching alarm clock
	 * @param context the context of the request
	 * @param alarmClock the alarm clock to query for
	 * @return the matching profile, or null if does not exist.
	 */
	static Profile TryGetProfileByAlarmClock(Context context, IAlarmClock alarmClock) {
		try {
			return GetProfileByAlarmClock(context, alarmClock);
		} catch (DataEntityDoesNotExistException e) {
			return null;
		}
	}
	
	/**
	 * Queries for a profile with the matching auto response
	 * @param context the context of the request
	 * @param matchingAutoResponse the matching response to query
	 * @return the matching profile.
	 * @throws DataEntityDoesNotExistException if the profile does not exist
	 */
	static Profile GetProfileByMatchingAutoResposne(Context context, IAutoResponse matchingAutoResponse) throws DataEntityDoesNotExistException {
		SQLiteDatabase db = OpenDBForReading(context);
		
		Cursor rulesItemCursor = GetRulesItemValuesByMatchingAutoResponse(db, matchingAutoResponse);
		rulesItemCursor.moveToNext();
		int rulesItemId = rulesItemCursor.getInt(rulesItemCursor.getColumnIndex(c_ColumnNameId));
		IAlarmClock alarmClock = GetAlarmClockFromCursor(context, rulesItemCursor);
		rulesItemCursor.close();
		db.close();
		
		return GetProfileById(context, rulesItemId, alarmClock, matchingAutoResponse);
	}
	
	/**
	 * Queries for a profile with the matching auto response
	 * @param context the context of the request
	 * @param matchingAutoResponse the auto response to query for
	 * @return the matching profile, or null if does not exist.
	 */
	static Profile TryGetProfileByMatchingAutoReposne(Context context, IAutoResponse matchingAutoResponse) {
		try {
			return GetProfileByMatchingAutoResposne(context, matchingAutoResponse);
		} catch (DataEntityDoesNotExistException e) {
			return null;
		}
	}
	
	/**
	 * Queries for a profile with the matching time frame
	 * @param context the context of the request
	 * @param timeFrame the matching time frame to query
	 * @return the matching profile.
	 * @throws DataEntityDoesNotExistException if the profile does not exist
	 */
	static Profile GetProfileByTimeFrame(Context context, ITimeFrame timeFrame) throws DataEntityDoesNotExistException {
		SQLiteDatabase db = OpenDBForReading(context);
		
		Cursor rulesItemCursor = GetRulesItemValuesByTimeFrame(context, db, timeFrame);
		rulesItemCursor.moveToNext();
		int rulesItemId = rulesItemCursor.getInt(rulesItemCursor.getColumnIndex(c_ColumnNameId));
		IAlarmClock alarmClock = GetAlarmClockFromCursor(context, rulesItemCursor);
		IAutoResponse matchingAutoResponse = GetMatchingAutoResponseFromCursor(context, rulesItemCursor);
		rulesItemCursor.close();
		db.close();
		
		return GetProfileById(context, rulesItemId, alarmClock, matchingAutoResponse);
	}
	
	/**
	 * Queries for a profile with the matching time frame
	 * @param context the context of the request
	 * @param timeFrame the matching time frame to query
	 * @return the matching profile or null if does not exist
	 */
	static Profile TryGetProfileByTimeFrame(Context context, ITimeFrame timeFrame) {
		try {
			return GetProfileByTimeFrame(context, timeFrame);
		} catch (DataEntityDoesNotExistException e) {
			return null;
		}
	}
	
	/**
	 * Queries for a profile with the non matching auto response
	 * @param context the context of the request
	 * @param nonMatchingAutoResponse the non matching response to query
	 * @return the matching profile.
	 * @throws DataEntityDoesNotExistException if the profile does not exist
	 */
	static Profile GetProfileByNonMatchingAutoResposne(Context context, IAutoResponse nonMatchingAutoResponse) throws DataEntityDoesNotExistException {
		return GetSingleProfile(context, c_ProfileColumnNameNonMatchingAutoResponse + "=" + nonMatchingAutoResponse.GetId());
	}
	
	/**
	 * Queries for a profile with the non matching auto response
	 * @param context the context of the request
	 * @param nonMatchingAutoResponse the auto response to query for
	 * @return the matching profile, or null if does not exist.
	 */
	static Profile TryGetProfileByNonMatchingAutoReposne(Context context, IAutoResponse nonMatchingAutoResponse) {
		try {
			return GetProfileByNonMatchingAutoResposne(context, nonMatchingAutoResponse);
		} catch (DataEntityDoesNotExistException e) {
			return null;
		}
	}
	
	/**
	 * Queries the DB for all profiles including the given contact
	 * @param context the context of the request
	 * @param contact the contact to query
	 * @return a collection of profiles containing the given contact
	 */
	static Set<IProfile> GetProfilesByContact(Context context, IContact contact) {
		SQLiteDatabase db = OpenDBForReading(context);
		Cursor cursor = db.query(
			ProfileToContactsTableName,
			c_QueryAllColumnsProfileToContacts,
			c_ProfileToContactsColumnNameContact + "=" + contact.GetId(),
			null, null, null, null);
		
		HashSet<IProfile> profiles = new HashSet<IProfile>();
		int profileId = 0;
		
		while (cursor.moveToNext()) {
			try {
				profileId = cursor.getInt(cursor.getColumnIndex(c_ProfileToContactsColumnNameProfile));
				profiles.add(GetProfileById(context, profileId));
			} catch (DataEntityDoesNotExistException e) {
				// This cannot happen since we retrieved the id from the DB. 
			}
		}
		
		cursor.close();
		db.close();
		return profiles;
	}
	
	/**
	 * Queries the DB for all active profiles
	 * @param context the context of the request
	 * @return all active profiles
	 */
	static Set<IProfile> GetActiveProfiles(Context context) {
		SQLiteDatabase db = OpenDBForReading(context);
		Cursor cursor = db.query(
			ProfileTableName,
			c_QueryAllColumnsProfile,
			c_ProfileColumnNameIsActive + "=" + 1,
			null, null, null, null);
		
		HashSet<IProfile> profiles = new HashSet<IProfile>();
		int profileId = 0;
		
		while (cursor.moveToNext()) {
			try {
				profileId = cursor.getInt(cursor.getColumnIndex(c_ColumnNameId));
				profiles.add(GetProfileById(context, profileId));
			} catch (DataEntityDoesNotExistException e) {
				// This cannot happen since we retrieved the id from the DB. 
			}
		}
		
		cursor.close();
		db.close();
		return profiles;
	}
	
	/**
	 * Queries the DB for all profiles including time frames matching a time range
	 * @param context the context of the request
	 * @param startTime the startTime to query
	 * @param endTime the end time to query
	 * @return a collection of profiles containing the given time range
	 */
	static Set<IProfile> GetProfilesByTimeRange(Context context, TimeInWeek startTime, TimeInWeek endTime) {
		Set<ITimeFrame> timeFramesByRange = new TimeFrameFactory().GetTimeFramesByTimeRange(context, startTime, endTime);
		HashSet<IProfile> profiles = new HashSet<IProfile>();
		
		int rulesItemId = 0;
		IProfile profile = null;
		for (ITimeFrame timeFrame : timeFramesByRange) {
			try {
				rulesItemId = GetRulesItemIdByTimeFrame(context, timeFrame);
				profile = TryGetProfileById(context, rulesItemId);
			} catch (DataEntityDoesNotExistException e) { }
			
			if (profile != null) {
				profiles.add(profile);
			}
		}
		
		return profiles;
	}
	
	// Private Methods
	
	/**
	 * Queries for a profile with the matching id
	 * @param context the context of the request
	 * @param id the id to query for
	 * @param alarmClock the alarm clock instance
	 * @param matchingAutoResponse the matching auto response instance
	 * @return the matching profile.
	 * @throws DataEntityDoesNotExistException if the profile does not exist
	 */
	private static Profile GetProfileById(
		Context context,
		int id,
		IAlarmClock alarmClock,
		IAutoResponse matchingAutoResponse) throws DataEntityDoesNotExistException {
		return GetSingleProfile(context, c_ColumnNameId + "=" + id, alarmClock, matchingAutoResponse);
	}
	
	/**
	 * Queries for a single profile using the where clause
	 * @param context the context of the request
	 * @param whereClause the where clause to use
	 * @return the matching profile instance.
	 * @throws DataEntityDoesNotExistException if the profile does not exist or if the matching rules item,
	 * alarm clock or auto responses do not exist.
	 */
	private static Profile GetSingleProfile(Context context, String whereClause) throws DataEntityDoesNotExistException {
		SQLiteDatabase db = OpenDBForReading(context);
		Cursor cursor = db.query(
			ProfileTableName,
			c_QueryAllColumnsProfile,
			whereClause,
			null, null, null, null);
		
		try {
			if (!cursor.moveToFirst()) {
				throw new DataEntityDoesNotExistException(Profile.class, whereClause);
			}
			
			return CreateProfileFromCursor(db, context, cursor);
		}
		finally {
			cursor.close();
			db.close();
		}
	}
	
	/**
	 * Queries for a single profile using the where clause
	 * @param context the context of the request
	 * @param whereClause the where clause to use
	 * @param alarmClock the alarm clock instance
	 * @param matchingAutoResponse the matching auto response instance
	 * @return the matching profile instance.
	 * @throws DataEntityDoesNotExistException if the profile does not exist or if the matching rules item,
	 * alarm clock or auto responses do not exist.
	 */
	private static Profile GetSingleProfile(
		Context context,
		String whereClause,
		IAlarmClock alarmClock,
		IAutoResponse matchingAutoResponse) throws DataEntityDoesNotExistException {
		
		SQLiteDatabase db = OpenDBForReading(context);
		Cursor cursor = db.query(
			ProfileTableName,
			c_QueryAllColumnsProfile,
			whereClause,
			null, null, null, null);
		
		try {
			if (!cursor.moveToFirst()) {
				throw new DataEntityDoesNotExistException(Profile.class, whereClause);
			}
			
			return CreateProfileFromCursor(db, context, cursor, alarmClock, matchingAutoResponse);
		}
		finally {
			cursor.close();
			db.close();
		}
	}
	
	/**
	 * Creates a profile instance using the values in the cursor.
	 * @param context the context of the request
	 * @param cursor the cursor containing the values. 
	 * @return the matching profile instance
	 * @throws DataEntityDoesNotExistException if a referenced entity or the matching rules item do not exist.
	 */
	private static Profile CreateProfileFromCursor(SQLiteDatabase db, Context context, Cursor cursor) throws DataEntityDoesNotExistException {
		int id = cursor.getInt(cursor.getColumnIndex(c_ColumnNameId));
		Cursor rulesItemCursor = GetRulesItemValuesById(db, id);
		if (!rulesItemCursor.moveToNext()) {
			throw new DataEntityDoesNotExistException(RulesItem.class, c_ColumnNameId + "=" + id);
		}
		
		IAutoResponse matchingAutoResponse = GetMatchingAutoResponseFromCursor(context, rulesItemCursor);
		IAlarmClock alarmClock = GetAlarmClockFromCursor(context, rulesItemCursor);
		rulesItemCursor.close();
		
		return CreateProfileFromCursor(db, context, cursor, alarmClock, matchingAutoResponse);
	}
	
	/**
	 * Creates a profile instance using the values in the cursor.
	 * @param context the context of the request
	 * @param cursor the cursor containing the values.
	 * @param alarmClock the alarm clock instance
	 * @param matchingAutoResponse the matching auto response instance
	 * @return the matching profile instance
	 * @throws DataEntityDoesNotExistException if a referenced entity or the matching rules item do not exist.
	 */
	private static Profile CreateProfileFromCursor(
		SQLiteDatabase db,
		Context context,
		Cursor cursor,
		IAlarmClock alarmClock,
		IAutoResponse matchingAutoResponse) throws DataEntityDoesNotExistException {
		
		int id = cursor.getInt(cursor.getColumnIndex(c_ColumnNameId));
		String name = cursor.getString(cursor.getColumnIndex(c_ProfileColumnNameName));
		
		int isActiveValue = cursor.getInt(cursor.getColumnIndex(c_ProfileColumnNameIsActive));
		boolean isActive = isActiveValue == 0 ? false : true;
		
		int nonMatchingAutoResponseId = cursor.getInt(cursor.getColumnIndex(c_ProfileColumnNameNonMatchingAutoResponse));
		IAutoResponse nonMatchingAutoResponse = new AutoResponseFactory().GetAutoResponseById(context, nonMatchingAutoResponseId);
		
		return new Profile(id, name, isActive, alarmClock, matchingAutoResponse, nonMatchingAutoResponse);
	}

	/**
	 * Initializes the new instance
	 * @param name the name of the profile
	 * @param isActive whether the profile is active or not
	 */
	private void Initialize(String name, boolean isActive) {
		m_Name = name;
		m_IsActive = isActive;
	}
	
	// IReferencingDataEntity implementation
	
	@Override
	public boolean IsReferenced(Context context, IReferencedDataEntity referencedEntity) {
		return super.IsReferenced(context, referencedEntity) ||
			referencedEntity == m_NonMatchingAutoResponseReference.GetReference() ||
			m_ContactReferences.GetReferences(context).contains(referencedEntity);
	}

	@Override
	public boolean CanRemoveReference(Context context, IReferencedDataEntity referencedEntity) {
		if (super.CanRemoveReference(context, referencedEntity) ||
			m_ContactReferences.GetReferences(context).contains(referencedEntity)) {
			// We can remove contacts from profiles
			return true;
		}
		
		// The non matching auto response is not nullable so we cannot remove it.
		return false;
	}

	@Override
	public void RemoveReference(Context context, IReferencedDataEntity referencedEntity) throws InvalidNullReferenceOnDeleteException, InvalidReferenceToRemoveException {
		super.RemoveReference(context, referencedEntity);
		
		if (!IsReferenced(context, referencedEntity)) {
			throw new InvalidReferenceToRemoveException(getClass(), GetId(), referencedEntity.getClass(), referencedEntity.GetId());
		}
		
		if (!CanRemoveReference(context, referencedEntity)) {
			throw new InvalidNullReferenceOnDeleteException(getClass(), GetId(), referencedEntity.getClass(), referencedEntity.GetId());
		}
		
		if (IContact.class.isInstance(referencedEntity)) {
			RemoveContact(context, (IContact)referencedEntity);
		}
	}
	
	// IDataEntity implementation

	@Override
	public void Refresh(Context context) throws DataEntityDoesNotExistException {
		Profile tempProfile = GetProfileById(context, GetId());
		Refresh(context, tempProfile);
		
		m_Name = tempProfile.GetName();
		m_IsActive = tempProfile.IsActive();
		m_ContactReferences.Refresh(context);
		
		try {
			m_NonMatchingAutoResponseReference.Set(tempProfile.GetNonMatchingAutoResponse());
		} catch (ReferenceCannotBeNullException e) { }
	}
	
	@Override
	public void Save(Context context) throws InvalidSaveDataEntityOperationException {
		if (IsNewInstance() && TryGetProfileByName(context, m_Name) != null) {
			throw new NamedProfileAlreadyExistsException(m_Name);
		}
		
		if (IsUpdated()) {
			// Saving the referenced objects before adding them to the profile in the DB.
			m_NonMatchingAutoResponseReference.SaveReferences(context);
			
			super.Save(context);
			
			// Adding contacts to the profile after saving the profile in the DB.
			m_ContactReferences.SaveReferences(context);
		}
	}
	
	@Override
	public void Delete(Context context) throws InvalidDeleteDataEntityOperationException, InvalidSaveDataEntityOperationException {
		// Removing contacts from profile before deletion
		m_ContactReferences.OnDeleteReferencingEntity(context);
		
		super.Delete(context);
		
		// Deleting references if needed.
		m_NonMatchingAutoResponseReference.OnDeleteReferencingEntity(context);
	}
	
	// DataEntity implementation

	@Override
	protected Hashtable<String, ContentValues> GetRecordValues() {
		Hashtable<String, ContentValues> valuesTable = new Hashtable<String, ContentValues>();
		for (Entry<String, ContentValues> entry : super.GetRecordValues().entrySet()) {
			valuesTable.put(entry.getKey(), entry.getValue());
		}
		
		ContentValues values = new ContentValues();
		values.put(c_ProfileColumnNameName, m_Name);
		values.put(c_ProfileColumnNameIsActive, m_IsActive ? 1 : 0);
		values.put(c_ProfileColumnNameNonMatchingAutoResponse, m_NonMatchingAutoResponseReference.GetReference().GetId());
		valuesTable.put(ProfileTableName, values);
		
		return valuesTable;
	}
	
	@Override
	protected Set<String> GetTableNames() {
		HashSet<String> tableNames = new HashSet<String>();
		for (String tableName : super.GetTableNames()) {
			tableNames.add(tableName);
		}
		
		tableNames.add(ProfileTableName);
		return tableNames;
	}
	
	// IReferencingMultipleDataEntity implementation

	@Override
	public void LoadReferences(Context context, Class<?> referencedType) {
		super.LoadReferences(context, referencedType);
		
		if (Contact.class == referencedType) {
			SQLiteDatabase db = OpenDBForReading(context);
			Cursor cursor = db.query(
				ProfileToContactsTableName,
				c_QueryAllColumnsProfileToContacts,
				c_ProfileToContactsColumnNameProfile + "=" + GetId(),
				null, null, null, null);
			
			int contactId = 0;
			IContact contact = null;
			ContactFactory factory = new ContactFactory();
			
			while (cursor.moveToNext()) {
				contactId = cursor.getInt(cursor.getColumnIndex(c_ProfileToContactsColumnNameContact));
				contact = factory.TryGetSingleContactById(context, contactId);
				if (contact == null) {
					contact = factory.TryGetGroupById(context, contactId);
				}
				
				m_ContactReferences.Add(context, contact);
			}
			
			cursor.close();
			db.close();
		}
	}

	@Override
	public void SaveReferenceConnection(Context context, IReferencedDataEntity referencedEntity) {
		super.SaveReferenceConnection(context, referencedEntity);
		
		if (IContact.class.isInstance(referencedEntity)) {
			SQLiteDatabase db = OpenDBForWriting(context);
			
			// Checking if the connection exists
			Cursor cursor = db.query(
				ProfileToContactsTableName,
				c_QueryAllColumnsProfileToContacts,
				c_ProfileToContactsColumnNameProfile + "=" + GetId() + " AND " + c_ProfileToContactsColumnNameContact + "=" + referencedEntity.GetId(),
				null, null, null, null);
			
			if (!cursor.moveToNext()) {
				// If no records were queried, add the connection between the contact and the profile
				ContentValues values = new ContentValues();
				values.put(c_ProfileToContactsColumnNameProfile, GetId());
				values.put(c_ProfileToContactsColumnNameContact, referencedEntity.GetId());
				
				db.insert(ProfileToContactsTableName, null, values);
			}
			
			cursor.close();
			db.close();
		}
	}

	@Override
	public void DeleteReferenceConnection(Context context, IReferencedDataEntity referencedEntity) {
		super.DeleteReferenceConnection(context, referencedEntity);
		
		if (IContact.class.isInstance(referencedEntity)) {
			SQLiteDatabase db = OpenDBForWriting(context);
			
			// Checking if the connection exists
			Cursor cursor = db.query(
				ProfileToContactsTableName,
				c_QueryAllColumnsProfileToContacts,
				c_ProfileToContactsColumnNameProfile + "=" + GetId() + " AND " + c_ProfileToContactsColumnNameContact + "=" + referencedEntity.GetId(),
				null, null, null, null);
			
			if (cursor.moveToNext()) {
				db.delete(
					ProfileToContactsTableName,
					c_ProfileToContactsColumnNameProfile + "=" + GetId() + " AND " + c_ProfileToContactsColumnNameContact + "=" + referencedEntity.GetId(),
					null);
			}
			
			cursor.close();
			db.close();
		}
	}

	@Override
	public ArrayList<IContact> GetContactsAsArrayList(Context context) {
		// TODO Auto-generated method stub
		return null;
	}
}
